<!DOCTYPE html>
<html xmlns:c="http://www.springframework.org/schema/util">
<head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LogView - 页面</title>
    <style>
        .log-container {
            padding: 15px;
        }

        .fix::after {
            clear: both;
            content: " ";
            display: block;
            font-size: 0;
            height: 0;
            visibility: hidden;
        }

        .log-container .log-box {
            margin: 0 auto;
            border-radius: 5px !important;
            box-shadow: 0 0 4px 1px #DEDEDE;
        }

        .log-box .panel-heading {
            padding: 3px 6px;
        }

        .log-container .log-box .log-control span {
            cursor: pointer;
            margin: 0 5px;
        }

        .log-container .log-box .log-control .log-filter {
            width: 500px;
            float: left;
        }

        .log-control .log-input,
        .log-control .log-input,
        .log-control .log-input {
            width: 150px !important;
            float: left !important;
            margin-left: 5px !important;
            border-radius: 5px !important;
            font-size: 12px !important;
        }

        .log-control .log-filter .form-control {
            background: transparent !important;
            height: auto !important;
        }

        .log-control .log-filter .btn {
            border-radius: 5px !important;
            font-size: 12px !important;
        }

        .log-container .log-box .log-control .log-keys {
            float: right;
            line-height: 32px;
            vertical-align: middle;
            text-align: right;
        }

        .log-container .log-box .log-control .log-keys .log-range {
            width: 80px !important;
            height: 30px !important;
        }

        .log-box .log-content {
            height: 400px;
            overflow-x: auto;
            overflow-y: auto;
            padding: 10px 15px;
            background: #2A2A2A;
            color: #B9B9B9;
        }

        .log-content .log-item {
            white-space: nowrap;
            font-size: 15px;
            font-weight: normal;
            opacity: 0.8;
        }

        .log-content .log-item p {
            padding: 1px;
        }

        .log-content .log-item .seq {
            min-width: 15px;
            display: inline-block;
        }

        .log-content .log-item:hover p {
            opacity: 1;
            cursor: pointer;
        }

        .log-content .log-item:hover p .seq {
            color: #E64331;
        }

        .log-item span {
            margin: 0 3px;
        }

        .log-item .level {
            color: #D1BF60;
        }

        .log-item .className {
            color: #2D958E;
        }

        .log-item .username {
            color: #2798C6;
        }

        .log-item .log-exception {
            padding-left: 10px;
        }

        .log-item .log-exception .stackTrace-message,
        .log-item .log-exception .stackTrace-item {
            display: block;
            vertical-align: middle;
        }

    </style>
    <link rel="stylesheet" href="/static/v3/css/bootstrap.min.css">
</head>

<body>
<div class="main">
    <div class="page-header" style="margin: 5px 0 0 0; padding-bottom:1px">
        <p class="text-center"><span>运行日志</span></p>
    </div>


    <div id="logContainer" class="log-container">
        <div class="panel panel-default log-box">
            <div class="panel-heading">
                <div class="log-control fix">
                    <div id="logFilter" class="log-filter fix">
                        <input id="keywords" class="keywords form-control log-input" placeholder="关键词过滤"/>
                        <input id="userSelector" class="keywords form-control log-input" placeholder="关键词过滤"/>
                        <select id="levelSelector"
                                class="levelSelector selectpicker show-tick form-control log-input">
                            <option value="">ALL</option>
                            <option value="TRACE">TRACE</option>
                            <option value="DEBUG" selected>DEBUG</option>
                            <option value="INFO">INFO</option>
                            <option value="WARN">WARN</option>
                            <option value="ERROR">ERROR</option>
                        </select>
                    </div>
                    <div id="logKeys" class="log-keys fix">
                        <input id="logRange" class="log-range form-control log-input" placeholder="1-X"/>
                        <span id="logCatalina" class="log-catalina"><i class="glyphicon glyphicon-play"></i></span>
                        <span id="logCopy" class="log-copy" title="点击复制"><i
                                class="glyphicon glyphicon-copy"></i></span>
                        <span id="logClear" class="log-clear"><i class="glyphicon glyphicon-trash"></i></span>
                    </div>
                </div>
            </div>
            <div class="log-content">
                <ul>

                </ul>
            </div>
        </div>


    </div>
</div>
</body>
<script type="text/javascript" src="/static/v3/js/common/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="/static/v3/js/common/bootstrap.min.js"></script>
<script type="text/javascript" src="/static/v3/js/websocket/sockjs.min.js"></script>
<script type="text/javascript" src="/static/v3/js/websocket/clipboard.min.js"></script>
<script type="text/javascript" src="/static/v3/js/websocket/stomp.js"></script>
<script type="text/javascript">

    //初始化控件
    // $('#levelSelector').selectpicker({noneSelectedText: "请选择日志级别"});

    //日志容器
    var logContainerJQ = $("#logContainer");
    var logContentJQ = logContainerJQ.find(".log-content>ul");

    //日志子项模板
    var logTemplate = '<li class="log-item" log-username="{{username}}" log-level="{{level}}" log-seq="{{seq}}">' +
        '<p class="log-message">' +
        '<span class="seq">{{seq}}</span>' +
        '<span class="username">{{username}}</span>' +
        '<span class="logTime">{{logTime}}</span>' +
        '<span class="threadName">{{threadName}}</span>' +
        '<span class="clientIp">{{clientIp}}</span>' +
        '<span class="level">{{level}}</span>' +
        '<span class="className">{{className}}</span>' +
        '<span class="methodName">{{methodName}}:{{lineNumber}}</span>' +
        '<span class="message">- {{message}}</span>' +
        '</p>{{exceptionStr}}</li>';

    //websocket
    var socket = new SockJS("/sock");
    var stompClient = Stomp.over(socket);

    //连接服务器
    stompClient.connect({}, function (frame) {
        logContentJQ.append('<li class="log-item">连接建立成功,请点击按键接收日志...</li>')

        //连接成功后,为logControl绑定事件
        var logCatalinaJQ = $("#logCatalina");

        //点击订阅或取消订阅日志
        logCatalinaJQ.on("click", function () {
            var _this = this;
            var iconJQ = $(_this).find("i.glyphicon");

            var hasStartClass = iconJQ.hasClass("glyphicon-play");
            if (hasStartClass) {
                //订阅日志
                _this.subscription = subscribeLog(stompClient, iconJQ, logContentJQ);
            } else {
                //取消订阅日志
                unsubscribeLog(_this.subscription, iconJQ, logContentJQ);
            }
        });

    }, function (error) {
        logContentJQ.append('连接失败【' + error + '】');
    });


    //订阅日志信息
    var seq = 0;

    function subscribeLog(stompClient, iconJQ, logContentJQ) {

        //公共订阅,服务器可以通过该地址发送信息给所有用户
        var subscription = stompClient.subscribe("/topic/runningLog", function (res) {
            if (res && res.body) {
                var logMessage = JSON.parse(res.body);

                //判断是否满足过滤信息,默认为满足
                //获取过滤选项的值
                var userArray = $('#userSelector').val();
                var levelArray = $('#levelSelector').selectpicker("val");
                var keywords = $('#keywords').val();
                var correct = true;
                if (null != userArray && userArray.length > 0) {
                    correct = $.inArray(logMessage.username, userArray) >= 0;
                }

                if (null != levelArray && levelArray.length > 0) {
                    correct = correct && $.inArray(logMessage.level, levelArray) >= 0;
                }

                if (null != keywords && $.trim(keywords).length > 0) {
                    correct = correct && (logMessage.clientIp.indexOf(keywords) >= 0 || logMessage.threadName.indexOf(keywords) >= 0
                        || logMessage.className.indexOf(keywords) >= 0 || logMessage.methodName.indexOf(keywords) >= 0
                        || logMessage.message.indexOf(keywords) >= 0
                        || logMessage.logTime.indexOf(keywords) >= 0
                        || (logMessage.exceptionStr ? logMessage.exceptionStr.indexOf(keywords) >= 0 : false));
                }

                if (correct) {
                    seq = seq + 1;
                    var logContent = logTemplate.replace(/\{\{seq\}\}/g, seq)
                        .replace(/\{\{username\}\}/g, logMessage.username)
                        .replace("{{logTime}}", logMessage.logTime)
                        .replace("{{threadName}}", logMessage.threadName)
                        .replace("{{clientIp}}", logMessage.clientIp)
                        .replace(/\{\{level\}\}/g, logMessage.level)
                        .replace("{{className}}", logMessage.className)
                        .replace("{{methodName}}", logMessage.methodName)
                        .replace("{{lineNumber}}", logMessage.lineNumber)
                        .replace("{{message}}", logMessage.message)
                        .replace("{{exceptionStr}}", logMessage.exceptionStr ? ('<div class="log-exception">' + logMessage.exceptionStr + '</div>') : "");
                    logContentJQ.append(logContent);
                }
            }
        });
        iconJQ.removeClass("glyphicon-play").addClass("glyphicon-stop");
        logContentJQ.append('<li class="log-item">开始接收日志...</li>');
        return subscription;
    }

    //取消订阅日志信息
    function unsubscribeLog(subscription, iconJQ, logContentJQ) {
        subscription.unsubscribe();
        iconJQ.removeClass("glyphicon-stop").addClass("glyphicon-play");
        logContentJQ.append('<li class="log-item">停止接收日志...</li>')
    }

    //清理日志
    $("#logClear").on("click", function () {
        logContentJQ.html("");
    });

    //点击复制内容
    var cb = new ClipboardJS('.log-copy', {
        text: function (trigger) {
            //获取行号
            var lineNumberBegin = 1;
            var lineNumberEnd = null;
            var logRangeStr = $("#logRange").val();
            var logRangeArray;
            if (null == logRangeStr || logRangeStr.length === 0) {
                logRangeArray = [];
            } else {
                logRangeArray = $.trim(logRangeStr).split("-");
            }

            //输入参数包含起止行号则进行赋值,否则使用不进行限制
            if (logRangeArray.length === 1) {
                try {
                    lineNumberBegin = parseInt(logRangeArray[0]);
                } catch (e) {
                    alert("行号无效");
                    return "";
                }
            }

            if (logRangeArray.length === 2) {
                try {
                    lineNumberBegin = parseInt(logRangeArray[0]);
                    lineNumberEnd = parseInt(logRangeArray[1]);
                } catch (e) {
                    alert("行号无效");
                    return "";
                }
            }

            var copyTextArray = [];
            logContentJQ.find(".log-item").each(function (index, element) {
                var eleJQ = $(element);
                var logSeq = eleJQ.attr("log-seq");
                var write = false;
                if (logSeq >= lineNumberBegin) {
                    write = lineNumberEnd ? (logSeq <= lineNumberEnd) : true;
                }

                //如果符合要求则写入
                if (write) {
                    var text = "";
                    eleJQ.find("span").each(function (i, e) {
                        text = text + $(e).text() + " ";
                    });
                    copyTextArray.push(text);
                }
            });
            return copyTextArray.join("\n");
        }
    });

    cb.on('success', function (e) {
        alert("复制成功!");
        e.clearSelection();
    });

    cb.on('error', function (e) {
        alert("复制失败,请重试或者您的浏览器可能不支持该操作!")
    });


    //页面初始化
    changeLogContentHeight();

    //监听浏览器窗口,动态调整控制台的高度
    $(window).resize(function () {
        changeLogContentHeight();
    });

    //根据窗口高度和可视元素调整logContent的高度
    function changeLogContentHeight() {
        //获取浏览器显示区域的高度及navbar、pageHeader的高度
        var windowHeight = $(window).height();
        var navbarHeight = $("#navbar").outerHeight(true);
        navbarHeight = !navbarHeight ? 60 : navbarHeight;
        var pageHeaderHeight = $(".main>.page-header").outerHeight(true);
        pageHeaderHeight = !pageHeaderHeight ? 50 : pageHeaderHeight;
        var panelHeadingHeight = $("#logContainer .panel-heading").outerHeight(true);
        panelHeadingHeight = !panelHeadingHeight ? 40 : panelHeadingHeight;

        //logContainer和logContent的填充距
        var paddingHeight = 15 * 2 + 10 * 2;

        //容错值
        var faultTolerant = 50;
        var logContentHeight = windowHeight - navbarHeight - pageHeaderHeight - panelHeadingHeight - paddingHeight - faultTolerant;

        //设置日志内容高度
        $("#logContainer .log-content").height(logContentHeight);
    }
</script>
</html>


